package com.ac.scum.log.task.face.log.counsumer.stages.gamplay;

import com.ac.common.core.id.SnowflakeIdGenerator;
import com.ac.scum.log.bean.Player;
import com.ac.scum.log.bean.PlayerAlarm;
import com.ac.scum.log.bean.PlayerUnlockScoreNotCounted;
import com.ac.scum.log.bean.ServerFTPLogLock;
import com.ac.scum.log.task.face.log.counsumer.LogConsumer;
import com.ac.scum.log.task.face.log.counsumer.PlayerAlarmInterface;
import com.ac.scum.log.task.face.log.counsumer.stages.GameplayLogConsumer;
import com.ac.scum.log.task.mapper.log.ServerFTPLogLockMapper;
import com.ac.scum.log.task.utils.map.GameMapZoneUtil;
import com.ac.scum.log.task.utils.queue.FTPQueueItem;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Service
@AllArgsConstructor
public class LockConsumer implements PlayerAlarmInterface {
    private final GameMapZoneUtil gameMapZoneUtil;
    private final SnowflakeIdGenerator snowflakeIdGenerator;
    private final ServerFTPLogLockMapper serverFTPLogLockMapper;
    private final RedisTemplate<String, Object> redisTemplate;

    public void lockConsumer(List<LogConsumer.GameLogLine> lines, FTPQueueItem ftpQueueItem, GameplayLogConsumer gameplayLogConsumer) {
        try {
            Pattern pattern = Pattern.compile("(\\d{4}\\.\\d{2}\\.\\d{2}-\\d{2}\\.\\d{2}\\.\\d{2}): \\[LogMinigame\\] \\[(.*?)\\] User: (.*?) \\((\\d+), (\\d+)\\). Success: (\\w+). Elapsed time: ([\\d.]+). Failed attempts: (\\d+). Target object: (.*?). Lock type: (.*?)\\. User owner: (.*?)\\. Location: X=([\\d.-]+) Y=([\\d.-]+) Z=([\\d.-]+)");
            Pattern ownerPattern = Pattern.compile("(\\d+)\\(\\[(\\d+)\\] (.*?)\\)");
            List<ServerFTPLogLock> logLocks = Lists.newArrayList();
            for (LogConsumer.GameLogLine line : lines) {
                Matcher matcher = pattern.matcher(line.getLineContext());
                if (matcher.find()) {
                    ServerFTPLogLock logLock = new ServerFTPLogLock();
                    logLock.setId(snowflakeIdGenerator.nextId());
                    logLock.setFileId(ftpQueueItem.getFileId());
                    logLock.setServerId(ftpQueueItem.getServer().getId());
                    logLock.setLineStart(line.getStartByte());
                    logLock.setLineEnd(line.getEndByte());
                    logLock.setCreateTime(LocalDateTime.now());
                    logLock.setServerTime(gameplayLogConsumer.parseTimestamp(matcher.group(1), ftpQueueItem));

                    logLock.setPlayerSteamId(Long.parseLong(matcher.group(5)));
                    Player player = gameplayLogConsumer.getOrInsertPlayerServer(logLock.getPlayerSteamId(), logLock.getServerId(), matcher.group(3));
                    logLock.setPlayerId(player.getId());
                    logLock.setSuccessFlag(matcher.group(6).trim().equals("Yes"));
                    logLock.setTime(Double.parseDouble(matcher.group(7)));
                    logLock.setFailedAttempts(Integer.parseInt(matcher.group(8)));
                    logLock.setTarget(matcher.group(9));
                    logLock.setLockType(getLockType(matcher.group(10).trim()));
                    String ownerInfo = matcher.group(11).trim();
                    logLock.setOwnerSteamId(0L);
                    logLock.setSystemLockFlag(true);
                    if (!ownerInfo.equals("0(World)") && !ownerInfo.equals("N/A")) {
                        Matcher ownerMatcher = ownerPattern.matcher(ownerInfo);
                        if (ownerMatcher.find()) {
                            logLock.setOwnerSteamId(Long.parseLong(ownerMatcher.group(2)));
                            Player ownerPlayer = gameplayLogConsumer.getOrInsertPlayerServer(logLock.getOwnerSteamId(), logLock.getServerId(), ownerMatcher.group(3));
                            logLock.setOwnerId(ownerPlayer.getId());
                            logLock.setSystemLockFlag(false);
                        }
                    }
                    logLock.setLocation(matcher.group(12) + "," + matcher.group(13) + "," + matcher.group(14));
                    logLock.setLocationZone(gameMapZoneUtil.getZone(Double.parseDouble(matcher.group(12)), Double.parseDouble(matcher.group(13))));
                    logLock.setCreateTime(LocalDateTime.now());
                    logLock.setCnTarget(gameplayLogConsumer.getSpawnItemCodeCnName(logLock.getTarget().toLowerCase().substring(0, logLock.getTarget().toLowerCase().indexOf("(id"))));
                    logLocks.add(logLock);

                    PlayerUnlockScoreNotCounted playerUnlockScoreNotCounted = new PlayerUnlockScoreNotCounted();
                    playerUnlockScoreNotCounted.setServerId(logLock.getServerId());
                    playerUnlockScoreNotCounted.setPlayerId(logLock.getPlayerId());
                    playerUnlockScoreNotCounted.setSteamId(logLock.getPlayerSteamId());
                    playerUnlockScoreNotCounted.setLockType(logLock.getLockType());
                    playerUnlockScoreNotCounted.setFailNum(logLock.getFailedAttempts());
                    playerUnlockScoreNotCounted.setFlag(logLock.getSuccessFlag());
                    playerUnlockScoreNotCounted.setUseTime(logLock.getTime());
                    playerUnlockScoreNotCounted.setCreateTime(logLock.getCreateTime());

                    playerUnlockScoreNotCounted.setScore(Math.max(0.0, Math.min(100.0, getScore(playerUnlockScoreNotCounted))));
                    String key = "unlock-score:" + playerUnlockScoreNotCounted.getServerId() + "-" + playerUnlockScoreNotCounted.getPlayerId();
                    redisTemplate.opsForList().rightPush(key, playerUnlockScoreNotCounted);
                } else {
                    gameplayLogConsumer.commitDiscardLog(ftpQueueItem, line, "lock no match");
                }
            }
            logLocks.forEach(serverFTPLogLockMapper::insert);

            List<PlayerAlarm> alarms = Lists.newArrayList();
            if (getCurrentAlarmConfig(ftpQueueItem).getLockEnabled()) {
                List<Long> hasAlarmPackagePlayerIds = getHasAlarmPackagePlayerIds(ftpQueueItem);
                if (!hasAlarmPackagePlayerIds.isEmpty()) {
                    logLocks.stream().filter(item -> hasAlarmPackagePlayerIds.contains(item.getOwnerId()) && item.getSuccessFlag()).forEach(item -> {
                        PlayerAlarm playerAlarm = new PlayerAlarm();
                        playerAlarm.setServerId(item.getServerId());
                        playerAlarm.setPlayerId(item.getOwnerId());
                        playerAlarm.setDetail("lock table");
                        playerAlarm.setLogId(item.getId());
                        playerAlarm.setLogTime(item.getServerTime());
                        playerAlarm.setServerName(ftpQueueItem.getServer().getName());
                        playerAlarm.setAlarmDetail(getLockTypeCn(item.getLockType()) + "已被破解");
                        playerAlarm.setAlarmItem(item.getLocationZone() + "-" + item.getCnTarget());
                        alarms.add(playerAlarm);
                    });
                }
            }
            commitAlarmTask(alarms);
        } catch (Exception e) {
            log.error("撬锁日志 出现异常", e);
        }
    }

    private static int getScore(PlayerUnlockScoreNotCounted playerUnlockScoreNotCounted) {
        final int baseScore = 100;
        final double STANDARD_TIME = 7.0;
        final double TIME_DECAY_RATE = 1.0;
        final int TIME_PENALTY_FACTOR = 5;
        final int FAIL_PENALTY_FACTOR = 5;
        int failPenalty = playerUnlockScoreNotCounted.getFailNum() * FAIL_PENALTY_FACTOR;
        double timePenalty = Math.max(0, (playerUnlockScoreNotCounted.getUseTime() - STANDARD_TIME) / TIME_DECAY_RATE) * TIME_PENALTY_FACTOR;
        return baseScore - (int) timePenalty - failPenalty;
    }

    private Integer getLockType(String str) {
        //锁类型 1.锈锁 2.铁索 3.银锁 4.金锁 5.密码锁
        return switch (str) {
            case "VeryEasy", "N/A" -> 1;
            case "Basic" -> 2;
            case "Medium" -> 3;
            case "Advanced" -> 4;
            case "DialLock" -> 5;
            default -> 9;
        };

    }

    private String getLockTypeCn(Integer lockType) {
        //锁类型 1.锈锁 2.铁锁 3.银锁 4.金锁 5.密码锁
        return switch (lockType) {
            case 1 -> "锈锁";
            case 2 -> "铁锁";
            case 3 -> "银锁";
            case 4 -> "金锁";
            case 5 -> "密码锁";
            default -> "未获取到锁类型";
        };

    }
}
